using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace Asteriods
{
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        enum GameState
        {
            StartScreen,
            Playing,
            GameOver,
        }
        GameState currentGameState;
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        SpriteFont font;
        const int INTIAL_AMOUNT_OF_ASTERIODS = 3;
        const int MAX_AMOUNT_OF_ASTERIODS = 10;
        const int MAX_ASTERIOD_SPEED = 2;
        const int BULLET_VELOCITY = 7;
        const int MAXIMUM_BULLET_LIFE_IN_MILLISECONDS = 1200;
        const string BULLET_IMAGE_PATH = "bullet";
        const string ASTERIOD_IMAGE_PATH = "asteroid";
        const string SHIP_IMAGE_PATH = "ship";
        const string FONT_PATH = "gameFont";
        int currentAmountOfAsteriods = INTIAL_AMOUNT_OF_ASTERIODS;
        string currentMessage = "";
        Vector2 currentMessagePosition = Vector2.Zero;
        List<Asteriod> activeAsteriods = new List<Asteriod>();
        List<Asteriod> inactiveAsteriods = new List<Asteriod>(MAX_AMOUNT_OF_ASTERIODS);
        List<Bullet> activeBullets = new List<Bullet>();
        List<Bullet> inactivebullets = new List<Bullet>();
        Input input;
        Random r = new Random();
        shipInput PlayerShip;
        int currentPlayerScore;
        Vector2 scoreDrawPosition = new Vector2(25, 25);
        Asteriod MediumAsteriod;
        Asteriod SmallAsteriod;
        
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            input = new Input(this);
            Components.Add(input);
            //IsMouseVisible = true;
            base.Initialize();
        }

        protected override void LoadContent()
        {
            font = Content.Load<SpriteFont>(FONT_PATH);
            spriteBatch = new SpriteBatch(GraphicsDevice);
            initializeGameObjectList(inactiveAsteriods, ASTERIOD_IMAGE_PATH);
            initializeStartScreen();
        }

        private void initializeStartScreen()
        {
            currentGameState = GameState.StartScreen;
            changeCurrentMessageToString("Click or Press Enter to Play.");
        }

        private void initializeGame()
        {
            changeCurrentMessageToString("");
            currentPlayerScore = -3;
            currentGameState = GameState.Playing;
            PlayerShip = new shipInput(this, SHIP_IMAGE_PATH, input);
            startNextLevel();
        }

        private void initializeGameOverScreen()
        {
            currentGameState = GameState.GameOver;
            changeCurrentMessageToString("Game Over.  \nScore: " + (currentPlayerScore * 10).ToString());
            PlayerShip.SetNewPositonVelocityandRotation(
                new Vector2(graphics.GraphicsDevice.Viewport.Width, graphics.GraphicsDevice.Viewport.Height) / 2,
                Vector2.Zero,
                MathHelper.Pi);
            currentAmountOfAsteriods = INTIAL_AMOUNT_OF_ASTERIODS;
            while (activeBullets.Count > 0)
                moveLists(activeBullets, inactivebullets, 0);
            while (activeAsteriods.Count > 0)
                moveLists(activeAsteriods, inactiveAsteriods, 0);
        }

        private void startNextLevel()
        {
            currentPlayerScore += currentAmountOfAsteriods;
            for (int i = 0; i < currentAmountOfAsteriods; i++)
            {
                moveLists(inactiveAsteriods, activeAsteriods, 0);
                Vector2 newposition = new Vector2(r.Next(graphics.GraphicsDevice.Viewport.Width), r.Next(graphics.GraphicsDevice.Viewport.Height));
                float newrotation = MathHelper.Lerp(0, MathHelper.TwoPi, (float)r.NextDouble());
                Vector2 newvelocity = new Vector2(MAX_ASTERIOD_SPEED * (float)Math.Cos(newrotation), MAX_ASTERIOD_SPEED * (float)Math.Sin(newrotation));
                activeAsteriods[i].SetNewPositonVelocityandRotation(newposition, newvelocity, newrotation);
            }
            PlayerShip.SetNewPositonVelocityandRotation(
                new Vector2(graphics.GraphicsDevice.Viewport.Width, graphics.GraphicsDevice.Viewport.Height) / 2,
                Vector2.Zero,
                MathHelper.Pi);
            currentAmountOfAsteriods++;
            if (currentAmountOfAsteriods > MAX_AMOUNT_OF_ASTERIODS)
                currentAmountOfAsteriods = INTIAL_AMOUNT_OF_ASTERIODS;
            while (activeBullets.Count > 0)
                moveLists(activeBullets, inactivebullets, 0);
        }

        private void moveLists(List<Asteriod> originalList, List<Asteriod> newList, int objectnumber)
        {
            newList.Add(originalList[objectnumber]);
            originalList.Remove(originalList[objectnumber]);
        }

        private void moveLists(List<Bullet> originalList, List<Bullet> newList, int objectnumber)
        {
            newList.Add(originalList[objectnumber]);
            originalList.Remove(originalList[objectnumber]);
        }

        private void changeCurrentMessageToString(string message)
        {
            currentMessage = message;
            currentMessagePosition = (new Vector2(
                graphics.GraphicsDevice.Viewport.Width,
                graphics.GraphicsDevice.Viewport.Height)
                - font.MeasureString(currentMessage)) / 2;
        }

        private List<Asteriod> initializeGameObjectList(List<Asteriod> Objects, string path)
        {
            for (int currentObjectCount = 0; currentObjectCount < Objects.Capacity; currentObjectCount++)
            {
                Objects.Add(new LargeAsteriod(this, path));
            }
            return Objects;
        }

        protected override void UnloadContent()
        {
        }


        bool isCollision(GameObject objectOne, GameObject objectTwo)
        {
            if (objectOne.returnHitBox().Intersects(objectTwo.returnHitBox()))
                return true;
            else
                return false;
        }

        protected override void Update(GameTime gameTime)
        {
            if (input.SingleKeyPress(Keys.Escape))
                Exit();
            switch (currentGameState)
            {
                case GameState.StartScreen:
                    if (isPlayerReady())
                        initializeGame();
                    break;
                case GameState.Playing:
                    if (activeAsteriods.Count == 0)
                        startNextLevel();
                    foreach (GameObject currentAsteriod in activeAsteriods)
                    {
                        currentAsteriod.Update(gameTime, graphics.GraphicsDevice);
                        if (isCollision(currentAsteriod, PlayerShip))
                        {
                            initializeGameOverScreen();
                            break;
                        }
                    }
                    updateBullets(gameTime);
                    PlayerShip.Update(gameTime, graphics.GraphicsDevice);
                    if (input.SingleLeftClick())
                        FireBullet();
                    break;
                case GameState.GameOver:
                    if (isPlayerReady())
                        initializeStartScreen();
                    break;
            }
            // TODO: Make each case a separate method

            base.Update(gameTime);
        }

        private bool isPlayerReady()
        {
            return input.SingleLeftClick() || input.SingleKeyPress(Keys.Enter);
        }

        private void updateBullets(GameTime gameTime)
        {
            for (int currentBullet = 0; currentBullet < activeBullets.Count; currentBullet++)
            {
                activeBullets[currentBullet].Update(gameTime, graphics.GraphicsDevice);
                if (activeBullets[currentBullet].MaximumMillisecondsOfLife < 0)
                {
                    moveLists(activeBullets, inactivebullets, currentBullet);
                    break;
                }
                for (int currentAsteriod = 0; currentAsteriod < activeAsteriods.Count; currentAsteriod++)
                {
                    if (isCollision(activeBullets[currentBullet], activeAsteriods[currentAsteriod]))
                    {
                        activeAsteriods[currentAsteriod].OnCollision(activeAsteriods, inactiveAsteriods, currentAsteriod, this, ASTERIOD_IMAGE_PATH);
                        //moveLists(activeAsteriods, inactiveAsteriods, currentAsteriod);
                        moveLists(activeBullets, inactivebullets, currentBullet);
                        currentPlayerScore += 1;
                        break;
                    }
                }
            }
        }

        private void FireBullet()
        {
            Vector2 newBulletVelocity = new Vector2((float)Math.Sin(PlayerShip.Rotation), -(float)Math.Cos(PlayerShip.Rotation)) * BULLET_VELOCITY;
            if (inactivebullets.Count == 0)
            {
                Bullet newbullet = new Bullet(this, BULLET_IMAGE_PATH, MAXIMUM_BULLET_LIFE_IN_MILLISECONDS);
                newbullet.SetNewPositonVelocityandRotation(PlayerShip.Position, PlayerShip.Velocity + newBulletVelocity, PlayerShip.Rotation);
                activeBullets.Add(newbullet);
            }
            else
            {
                moveLists(inactivebullets, activeBullets, 0);
                activeBullets[activeBullets.Count - 1].MaximumMillisecondsOfLife = MAXIMUM_BULLET_LIFE_IN_MILLISECONDS;
                activeBullets[activeBullets.Count - 1].SetNewPositonVelocityandRotation(PlayerShip.Position, PlayerShip.Velocity + newBulletVelocity, PlayerShip.Rotation);
            }
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);
            spriteBatch.Begin();
            spriteBatch.DrawString(font, currentMessage, currentMessagePosition, Color.White);
            if (currentGameState == GameState.Playing)
            {
                spriteBatch.DrawString(font, "Score: " + (currentPlayerScore * 10).ToString(), scoreDrawPosition, Color.WhiteSmoke);
                foreach (GameObject currentAsteriod in activeAsteriods)
                {
                    currentAsteriod.Draw(gameTime, spriteBatch);
                }
                foreach (GameObject currentBullet in activeBullets)
                {
                    currentBullet.Draw(gameTime, spriteBatch);
                }
                PlayerShip.Draw(gameTime, spriteBatch);
            }
            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}
